#include "core/rpc_plugin.h"

#include "core/rpc_generator.h"

namespace asfinger {
namespace pbrpc {

bool RpcGenerator::Generate(const pb::FileDescriptor *file,
                            const std::string &parameter,
                            pbc::GeneratorContext *generator_context,
                            std::string *error) const {
  if (file->options().cc_generic_services()) {
    *error =
        "pbrpc proto compiler plugin does not work with generic "
        "services. To generate cpp rpc APIs, please set \""
        "cc_generic_service = false\".";
    return false;
  }

  Parameters generator_parameters;
  generator_parameters.use_system_headers = true;

  ProtoBufFile pb_file(file);

  if (!parameter.empty()) {
    std::vector<std::string> parameters_list = Tokenize(parameter, ",");

    for (auto &parameter_string : parameters_list) {
      std::vector<std::string> param = Tokenize(parameter_string, "=");
      if ("services_namespace" == param[0]) {
        generator_parameters.services_namespace = param[1];
      } else if ("use_system_headers" == param[0]) {
        if ("true" == param[1]) {
          generator_parameters.use_system_headers = true;
        } else if ("false" == param[1]) {
          generator_parameters.use_system_headers = false;
        } else {
          *error = std::string("Invalid parameter: ") + parameter_string;
          return false;
        }
      } else if ("rpc_search_path" == param[0]) {
        generator_parameters.rpc_search_path = param[1];
      } else if ("additional_header_includes" == param[0]) {
        generator_parameters.additional_header_includes =
            Tokenize(param[1], ":");
      } else {
        *error = std::string("Unknown parameter: ") + parameter_string;
        return false;
      }
    }
  }

  std::string file_name = StripProto(file->name());

  // 头文件编码
  std::string header_code =
      GetHeaderPrologue(&pb_file, generator_parameters) +
          GetHeaderIncludes(&pb_file, generator_parameters) +
          GetHeaderServices(&pb_file, generator_parameters) +
          GetHeaderEpilogue(&pb_file, generator_parameters);
  std::unique_ptr<pb::io::ZeroCopyOutputStream> header_output(
      generator_context->Open(file_name + ".rpc.pb.h"));
  pb::io::CodedOutputStream header_coded_out(header_output.get());
  header_coded_out.WriteRaw(header_code.data(),
                            static_cast<int>(header_code.size()));

  // 源文件编码
  std::string source_code =
      GetSourcePrologue(&pb_file, generator_parameters) +
          GetSourceIncludes(&pb_file, generator_parameters) +
          GetSourceServices(&pb_file, generator_parameters) +
          GetSourceEpilogue(&pb_file, generator_parameters);
  std::unique_ptr<pb::io::ZeroCopyOutputStream> source_output(
      generator_context->Open(file_name + ".rpc.pb.cc"));
  pb::io::CodedOutputStream source_coded_out(source_output.get());
  source_coded_out.WriteRaw(source_code.data(),
                            static_cast<int>(source_code.size()));

  return true;
}

void RpcGenerator::Insert(pbc::GeneratorContext *context,
                          const std::string &filename,
                          const std::string &insertion_point,
                          const std::string &code) const {
  std::unique_ptr<pb::io::ZeroCopyOutputStream> output(
      context->OpenForInsert(filename, insertion_point));
  pb::io::CodedOutputStream coded_out(output.get());
  coded_out.WriteRaw(code.data(), static_cast<int>(code.size()));
}

} //! namespace pbrpc
} //! namespace asfinger
